import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import re
import os
import pyperclip  # 用于复制到剪贴板


class BibToItemConverter:
    def __init__(self, root):
        self.root = root
        self.root.title("BibTeX转BibItem转换器")
        self.root.geometry("1000x700")
        self.root.minsize(800, 600)  # 设置最小窗口大小，确保UI元素都有足够空间

        # 变量初始化
        self.bib_file = ""
        self.entries = []
        self.bibitems = []

        # 类型映射
        self.type_mapping = {
            'article': {'label': '期刊论文', 'code': 'J'},
            'phdthesis': {'label': '学位论文', 'code': 'D'},
            'book': {'label': '书籍', 'code': 'M'},
            'manual': {'label': '手册', 'code': 'M'},
            'techreport': {'label': '技术报告', 'code': 'R'},
            'inproceedings': {'label': '会议论文', 'code': 'C'},
            'conference': {'label': '会议论文', 'code': 'C'},
            'incollection': {'label': '书籍章节', 'code': 'M'},
            'patent': {'label': '专利', 'code': 'P'},
            'misc': {'label': '其他', 'code': 'Z'}
        }

        # 创建UI界面
        self.create_widgets()

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill="both", expand=True)

        # 文件选择框架
        file_frame = ttk.LabelFrame(main_frame, text="选择BibTeX文件")
        file_frame.pack(fill="x", padx=10, pady=5)

        # BibTeX文件选择
        ttk.Label(file_frame, text="BibTeX文件:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
        self.bib_file_var = tk.StringVar()
        ttk.Entry(file_frame, textvariable=self.bib_file_var, width=60, state="readonly").grid(row=0, column=1, padx=5,
                                                                                               pady=5)
        ttk.Button(file_frame, text="浏览...", command=self.select_bib_file).grid(row=0, column=2, padx=5, pady=5)
        ttk.Button(file_frame, text="加载文件", command=self.load_bib_file).grid(row=0, column=3, padx=5, pady=5)

        # 控制按钮框架 (放在顶部)
        ctrl_button_frame = ttk.Frame(main_frame)
        ctrl_button_frame.pack(fill="x", padx=10, pady=5)

        ttk.Button(ctrl_button_frame, text="转换所有条目", command=self.convert_all_entries, width=15).pack(side="left",
                                                                                                      padx=5, pady=5)
        ttk.Button(ctrl_button_frame, text="转换选中条目", command=self.convert_selected_entries, width=15).pack(side="left",
                                                                                                           padx=5,
                                                                                                           pady=5)
        ttk.Button(ctrl_button_frame, text="复制到剪贴板", command=self.copy_to_clipboard, width=15).pack(side="left", padx=5,
                                                                                                    pady=5)
        ttk.Button(ctrl_button_frame, text="保存为文件", command=self.save_to_file, width=15).pack(side="right", padx=5,
                                                                                              pady=5)

        # 分割窗口 - 减小高度以确保按钮可见
        paned_window = ttk.PanedWindow(main_frame, orient="vertical")
        paned_window.pack(fill="both", expand=True, padx=10, pady=10)

        # 条目列表框架
        entries_frame = ttk.LabelFrame(paned_window, text="BibTeX条目")
        paned_window.add(entries_frame, weight=1)

        # 条目列表
        entries_tree_frame = ttk.Frame(entries_frame)
        entries_tree_frame.pack(fill="both", expand=True, padx=5, pady=5)

        self.entries_tree = ttk.Treeview(entries_tree_frame, columns=("ID", "类型", "标题", "作者", "年份"), show="headings")
        self.entries_tree.pack(side="left", fill="both", expand=True)

        # 配置列
        self.entries_tree.heading("ID", text="ID")
        self.entries_tree.heading("类型", text="类型")
        self.entries_tree.heading("标题", text="标题")
        self.entries_tree.heading("作者", text="作者")
        self.entries_tree.heading("年份", text="年份")

        self.entries_tree.column("ID", width=150)
        self.entries_tree.column("类型", width=80)
        self.entries_tree.column("标题", width=300)
        self.entries_tree.column("作者", width=250)
        self.entries_tree.column("年份", width=50)

        # 添加滚动条
        scrollbar = ttk.Scrollbar(entries_tree_frame, orient="vertical", command=self.entries_tree.yview)
        scrollbar.pack(side="right", fill="y")
        self.entries_tree.configure(yscrollcommand=scrollbar.set)

        # BibItem结果框架
        bibitem_frame = ttk.LabelFrame(paned_window, text="BibItem结果")
        paned_window.add(bibitem_frame, weight=1)

        # BibItem结果文本区域
        bibitem_text_frame = ttk.Frame(bibitem_frame)
        bibitem_text_frame.pack(fill="both", expand=True, padx=5, pady=5)

        self.bibitem_text = tk.Text(bibitem_text_frame, wrap="word")
        self.bibitem_text.pack(side="left", fill="both", expand=True)

        # 添加滚动条
        scrollbar2 = ttk.Scrollbar(bibitem_text_frame, orient="vertical", command=self.bibitem_text.yview)
        scrollbar2.pack(side="right", fill="y")
        self.bibitem_text.configure(yscrollcommand=scrollbar2.set)

        # 底部状态框架
        status_frame = ttk.Frame(main_frame)
        status_frame.pack(fill="x", padx=10, pady=5)
        status_label = ttk.Label(status_frame, text="准备就绪")
        status_label.pack(side="left")

    def select_bib_file(self):
        """选择BibTeX文件"""
        self.bib_file = filedialog.askopenfilename(
            title="选择BibTeX文件",
            filetypes=[("BibTeX文件", "*.bib"), ("所有文件", "*.*")]
        )
        if self.bib_file:
            self.bib_file_var.set(self.bib_file)

    def load_bib_file(self):
        """加载并解析BibTeX文件"""
        if not self.bib_file:
            messagebox.showwarning("警告", "请先选择BibTeX文件。")
            return

        try:
            # 读取BibTeX文件
            with open(self.bib_file, 'r', encoding='utf-8') as bibtex_file:
                content = bibtex_file.read()

            # 清空条目列表
            self.entries = []

            # 清空树形视图
            for item in self.entries_tree.get_children():
                self.entries_tree.delete(item)

            # 使用正则表达式解析BibTeX条目
            # 这个正则表达式寻找@type{id, ... }模式，正确处理嵌套大括号
            entry_pattern = r'@(\w+)\s*\{\s*([^,]+),\s*([^@]+?)(?=\s*@|\s*\Z)'

            for match in re.finditer(entry_pattern, content, re.DOTALL):
                entry_type = match.group(1).lower()
                entry_id = match.group(2).strip()
                entry_content = match.group(3)

                # 获取用于显示的重要字段
                title = self.extract_field(entry_content, 'title')
                author = self.extract_field(entry_content, 'author')
                year = self.extract_field(entry_content, 'year')

                # 存储条目信息
                entry = {
                    'id': entry_id,
                    'type': entry_type,
                    'title': title,
                    'author': author,
                    'year': year,
                    'content': entry_content,
                    'raw': match.group(0)
                }

                self.entries.append(entry)

                # 获取类型标签
                type_label = self.type_mapping.get(entry_type, {'label': entry_type, 'code': 'Z'})['label']

                # 添加到树形视图
                self.entries_tree.insert('', 'end',
                                         values=(entry_id, type_label, title[:50] + ('...' if len(title) > 50 else ''),
                                                 author[:50] + ('...' if len(author) > 50 else ''), year))

            messagebox.showinfo("信息", f"已成功加载{len(self.entries)}个BibTeX条目。")

        except Exception as e:
            messagebox.showerror("错误", f"加载BibTeX文件时出错: {str(e)}")

    def extract_field(self, content, field_name):
        """从BibTeX条目内容中提取字段"""
        # 匹配花括号或引号中的内容
        field_pattern = fr'{field_name}\s*=\s*(?:\{{(.*?)\}}|(\".*?\")|(\d+))'
        match = re.search(field_pattern, content, re.IGNORECASE | re.DOTALL)

        if match:
            # 返回第一个非None的组
            for group in match.groups():
                if group is not None:
                    return group.strip('"')
            return ""
        return ""

    def format_authors(self, authors_str):
        """格式化作者名，使用大写，并限制为前三个作者"""
        if not authors_str:
            return ""

        # 分割作者名
        authors = []
        current_author = ""
        brace_level = 0

        for char in authors_str:
            if char == '{':
                brace_level += 1
                if brace_level > 1:  # 只有当我们已经在一个大括号内时才添加
                    current_author += char
            elif char == '}':
                brace_level -= 1
                if brace_level >= 1:  # 只有当我们仍在一个大括号内时才添加
                    current_author += char
            elif char == ',' and brace_level == 0:  # 只在最顶层拆分
                authors.append(current_author.strip())
                current_author = ""
            elif char == 'a' and current_author.endswith(' and') and brace_level == 0:
                # 处理 "and" 连接符
                current_author = current_author[:-3].strip()
                if current_author:
                    authors.append(current_author)
                current_author = ""
            elif char == 'd' and len(current_author) >= 3 and current_author[-3:] == ' an' and brace_level == 0:
                current_author += char
            elif char == ' ' and len(current_author) >= 4 and current_author[-4:] == ' and' and brace_level == 0:
                # 结束了 "and"，新作者开始
                current_author = ""
            else:
                current_author += char

        # 添加最后一个作者
        if current_author.strip():
            authors.append(current_author.strip())

        # 处理每个作者名
        formatted_authors = []
        for author in authors:
            # 移除花括号
            author = re.sub(r'\{|\}', '', author)

            # 分割姓和名
            if ',' in author:
                # 如果格式是 "Last, First"
                parts = author.split(',', 1)
                last_name = parts[0].strip()
                first_name = parts[1].strip() if len(parts) > 1 else ""

                # 提取首字母
                first_initials = ''.join(word[0] for word in first_name.split() if word)

                formatted_author = f"{last_name.upper()} {first_initials}"
            else:
                # 如果格式是 "First Last"
                parts = author.split()
                if len(parts) > 1:
                    last_name = parts[-1]
                    first_name = ' '.join(parts[:-1])

                    # 提取首字母
                    first_initials = ''.join(word[0] for word in first_name.split() if word)

                    formatted_author = f"{last_name.upper()} {first_initials}"
                else:
                    # 只有一个名字
                    formatted_author = author.upper()

            formatted_authors.append(formatted_author.strip())

        # 限制为前三个作者
        if len(formatted_authors) > 3:
            return ', '.join(formatted_authors[:3]) + ' et al'
        else:
            return ', '.join(formatted_authors)

    def convert_to_bibitem(self, entry):
        """将BibTeX条目转换为BibItem格式"""
        entry_type = entry['type'].lower()
        entry_id = entry['id']

        # 提取常用字段
        title = self.extract_field(entry['content'], 'title').strip('{}')
        authors = self.format_authors(self.extract_field(entry['content'], 'author'))
        year = self.extract_field(entry['content'], 'year')

        # 获取类型代码
        type_info = self.type_mapping.get(entry_type, {'label': '其他', 'code': 'Z'})
        type_code = type_info['code']

        bibitem = f"\\bibitem{{{entry_id}}}\n"

        # 根据类型格式化
        if entry_type == 'article':
            # 期刊论文格式: 作者. 文题[J]. 期刊名, 年, 卷(期): 起-止页码.
            journal = self.extract_field(entry['content'], 'journal')
            volume = self.extract_field(entry['content'], 'volume')
            number = self.extract_field(entry['content'], 'number')
            pages = self.extract_field(entry['content'], 'pages').replace('--', '-')
            doi = self.extract_field(entry['content'], 'doi')

            volume_issue = f"{volume}"
            if number:
                volume_issue += f"({number})"

            bibitem += f"{authors}. {title}[J]. {journal}, {year}"

            if volume_issue:
                bibitem += f", {volume_issue}"

            if pages:
                bibitem += f": {pages}"

            bibitem += "."

            if doi:
                pass
                # bibitem += f" DOI:{doi}."

        elif entry_type in ['inproceedings', 'conference']:
            # 会议论文格式: 作者. 文题[C]. 会议名, 会议地, 会议年: 起-止页码.
            booktitle = self.extract_field(entry['content'], 'booktitle')
            pages = self.extract_field(entry['content'], 'pages').replace('--', '-')
            address = self.extract_field(entry['content'], 'address')
            doi = self.extract_field(entry['content'], 'doi')

            bibitem += f"{authors}. {title}[C]. {booktitle}"

            if address:
                bibitem += f", {address}"

            bibitem += f", {year}"

            if pages:
                bibitem += f": {pages}"

            bibitem += "."

            if doi:
                pass
                # bibitem += f" DOI:{doi}."

        elif entry_type == 'book':
            # 普通图书格式: 作者. 书名[M]. 译者. 版本. 出版地: 出版者, 出版年: 起-止页码.
            publisher = self.extract_field(entry['content'], 'publisher')
            address = self.extract_field(entry['content'], 'address')
            edition = self.extract_field(entry['content'], 'edition')

            bibitem += f"{authors}. {title}[M]"

            if edition:
                bibitem += f". {edition} ed."

            if address and publisher:
                bibitem += f". {address}: {publisher}"
            elif publisher:
                bibitem += f". {publisher}"

            bibitem += f", {year}."

        elif entry_type == 'phdthesis':
            # 学位论文格式: 作者. 文题[D]. 授位单位所在地: 授位单位, 授位年: 起-止页码.
            school = self.extract_field(entry['content'], 'school')
            address = self.extract_field(entry['content'], 'address')

            bibitem += f"{authors}. {title}[D]"

            if address and school:
                bibitem += f". {address}: {school}"
            elif school:
                bibitem += f". {school}"

            bibitem += f", {year}."

        elif entry_type == 'patent':
            # 专利格式: 发明人. 专利名: 专利号[P]. 授权日期.
            number = self.extract_field(entry['content'], 'number')
            month = self.extract_field(entry['content'], 'month')
            day = self.extract_field(entry['content'], 'day')

            bibitem += f"{authors}. {title}: {number}[P]"

            date = year
            if month:
                date += f"-{month}"
            if day:
                date += f"-{day}"

            bibitem += f". {date}."

        else:
            # 其他格式: 作者. 标题[Z]. 出版地: 出版者, 出版年.
            publisher = self.extract_field(entry['content'], 'publisher')
            address = self.extract_field(entry['content'], 'address')
            url = self.extract_field(entry['content'], 'url')

            bibitem += f"{authors}. {title}[{type_code}]"

            if address and publisher:
                bibitem += f". {address}: {publisher}"
            elif publisher:
                bibitem += f". {publisher}"

            bibitem += f", {year}."

            if url:
                bibitem += f" URL:{url}."

        return self.balance_brackets(bibitem)

    def balance_brackets(self, text):
        """
        Check if brackets (parentheses, square brackets, curly braces) in the text are balanced.
        If not, remove unmatched brackets.

        Args:
            text (str): Input text containing brackets

        Returns:
            str: Text with unmatched brackets removed
        """
        # Define bracket pairs
        brackets = {'(': ')', '[': ']', '{': '}'}

        # First pass: identify positions of unmatched brackets
        stack = []
        to_remove = set()

        for i, char in enumerate(text):
            if char in brackets.keys():  # Left bracket
                stack.append((char, i))
            elif char in brackets.values():  # Right bracket
                if not stack or brackets[stack[-1][0]] != char:
                    # Unmatched right bracket, mark for removal
                    to_remove.add(i)
                else:
                    # Matched bracket, pop from stack
                    stack.pop()

        # Any remaining brackets in stack are unmatched left brackets
        for _, pos in stack:
            to_remove.add(pos)

        # Second pass: build result string without unmatched brackets
        result = ''.join(char for i, char in enumerate(text) if i not in to_remove)

        return result

    def convert_all_entries(self):
        """转换所有BibTeX条目为BibItem格式"""
        if not self.entries:
            messagebox.showwarning("警告", "没有可转换的条目。请先加载BibTeX文件。")
            return

        self.bibitems = []

        # 清空结果文本区域
        self.bibitem_text.delete(1.0, tk.END)

        try:
            for entry in self.entries:
                bibitem = self.convert_to_bibitem(entry)
                self.bibitems.append(bibitem)
                self.bibitem_text.insert(tk.END, bibitem + "\n\n")

            messagebox.showinfo("成功", f"已成功转换{len(self.bibitems)}个BibTeX条目为BibItem格式。")
        except Exception as e:
            messagebox.showerror("错误", f"转换条目时出错: {str(e)}")

    def convert_selected_entries(self):
        """转换选中的BibTeX条目为BibItem格式"""
        selected_items = self.entries_tree.selection()

        if not selected_items:
            messagebox.showwarning("警告", "请先选择要转换的条目。")
            return

        self.bibitems = []

        # 清空结果文本区域
        self.bibitem_text.delete(1.0, tk.END)

        try:
            for i, item in enumerate(selected_items):
                # 获取条目ID
                entry_id = self.entries_tree.item(item, "values")[0]

                # 查找对应的条目
                entry = next((e for e in self.entries if e['id'] == entry_id), None)

                if entry:
                    bibitem = self.convert_to_bibitem(entry)
                    self.bibitems.append(bibitem)
                    self.bibitem_text.insert(tk.END, bibitem + "\n\n")

            messagebox.showinfo("成功", f"已成功转换{len(self.bibitems)}个BibTeX条目为BibItem格式。")
        except Exception as e:
            messagebox.showerror("错误", f"转换条目时出错: {str(e)}")

    def copy_to_clipboard(self):
        """复制BibItem结果到剪贴板"""
        text = self.bibitem_text.get(1.0, tk.END)

        if not text.strip():
            messagebox.showwarning("警告", "没有可复制的内容。请先转换BibTeX条目。")
            return

        pyperclip.copy(text)
        messagebox.showinfo("成功", "已成功复制BibItem结果到剪贴板。")

    def save_to_file(self):
        """保存BibItem结果到文件"""
        text = self.bibitem_text.get(1.0, tk.END)

        if not text.strip():
            messagebox.showwarning("警告", "没有可保存的内容。请先转换BibTeX条目。")
            return

        # 请求保存位置
        output_file = filedialog.asksaveasfilename(
            title="保存BibItem结果",
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("LaTeX文件", "*.tex"), ("所有文件", "*.*")],
            initialdir=os.path.dirname(self.bib_file) if self.bib_file else None,
            initialfile="bibitems.txt"
        )

        if not output_file:
            return

        try:
            with open(output_file, 'w', encoding='utf-8') as f:
                f.write(text)

            messagebox.showinfo("成功", f"已成功保存BibItem结果到{output_file}。")
        except Exception as e:
            messagebox.showerror("错误", f"保存文件时出错: {str(e)}")


if __name__ == "__main__":
    root = tk.Tk()
    app = BibToItemConverter(root)
    root.mainloop()
